﻿Imports System.Math
Imports System.Web

Public Module CoordinateConverter
    Private Const x_pi As Double = 3.14159265358979 * 3000 / 180
    Private Const pi As Double = 3.14159265358979
    Private Const a As Double = 6378245
    Private Const ee As Double = 0.00669342162296594

    Private Function fromlat(ByVal lng As Double, ByVal lat As Double) As Double
        Dim tmp As Double
        tmp = -100 + 2 * lng + 3 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Sqrt(Abs(lng))
        tmp += (20 * Sin(6 * lng * pi) + 20 * Sin(2 * lng * pi)) * 2 / 3
        tmp += (20 * Sin(lat * pi) + 40 * Sin(lat / 3 * pi)) * 2 / 3
        tmp += (160 * Sin(lat / 12 * pi) + 320 * Sin(lat * pi / 30)) * 2 / 3
        Return tmp
    End Function

    Private Function fromlng(ByVal lng As Double, ByVal lat As Double) As Double
        Dim tmp As Double
        tmp = 300 + lng + 2 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Sqrt(Abs(lng))
        tmp += (20 * Sin(6 * lng * pi) + 20 * Sin(2 * lng * pi)) * 2 / 3
        tmp += (20 * Sin(lng * pi) + 40 * Sin(lng / 3 * pi)) * 2 / 3
        tmp += (150 * Sin(lng / 12 * pi) + 300 * Sin(lng / 30 * pi)) * 2 / 3
        Return tmp
    End Function

    Private Function out_of_china(ByVal lng As Double, ByVal lat As Double) As Boolean
        Return (lng > 73.66 AndAlso lng < 135.05 AndAlso lat > 3.86 AndAlso lat < 53.55)
    End Function

    Public Function f_gcj02_to_wgs84(ByVal lng As Double, ByVal lat As Double) As (wgslng As Double, wgslat As Double)
        If Not out_of_china(lng, lat) Then
            Return (Double.NaN, Double.NaN)
        End If

        Dim dlat = fromlat(lng - 105, lat - 35)
        Dim dlng = fromlng(lng - 105, lat - 35)
        Dim radlat = lat / 180 * pi
        Dim magic = Sin(radlat)
        magic = 1 - ee * magic * magic
        Dim sqrtmagic = Sqrt(magic)
        dlat = (dlat * 180) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
        dlng = (dlng * 180) / (a / sqrtmagic * Cos(radlat) * pi)
        Dim mglat = lat + dlat
        Dim mglng = lng + dlng

        Dim wgslng = lng * 2 - mglng
        Dim wgslat = lat * 2 - mglat

        Return (wgslng, wgslat)
    End Function

    Public Function ParseAndConvertGcj02ToWgs84(input As String) As (address As String, longitude As Double, latitude As Double)
        If String.IsNullOrEmpty(input) Then
            Return (String.Empty, Double.NaN, Double.NaN)
        End If

        Dim result = ParseMapUrl(input)
        Dim address = result.address
        Dim longitude = result.longitude
        Dim latitude = result.latitude

        If Double.IsNaN(longitude) OrElse Double.IsNaN(latitude) Then
            Return (address, Double.NaN, Double.NaN)
        End If

        Try
            Dim result2 = f_gcj02_to_wgs84(longitude, latitude)
            longitude = result2.wgslng
            latitude = result2.wgslat
            Return (address, longitude, latitude)
        Catch ex As Exception
            Return (String.Empty, Double.NaN, Double.NaN)
        End Try
    End Function


    Public Function ParseMapUrl(url As String) As (address As String, longitude As Double, latitude As Double)
        ' 初始化默认值
        Dim address As String = ""
        Dim longitude As Double = Double.NaN
        Dim latitude As Double = Double.NaN

        Try
            ' 获取查询字符串部分
            Dim queryStart = url.IndexOf("?")
            If queryStart < 0 Then Return (address, longitude, latitude)

            Dim queryString = url.Substring(queryStart + 1)
            Dim params = queryString.Split("&"c)

            ' 解析参数
            For Each param In params
                Dim parts = param.Split("="c)
                If parts.Length <> 2 Then Continue For

                Select Case parts(0).ToLower()
                    Case "name"
                        address = HttpUtility.UrlDecode(parts(1))
                    Case "pointx"
                        Double.TryParse(parts(1), longitude)
                    Case "pointy"
                        Double.TryParse(parts(1), latitude)
                End Select
            Next
        Catch ex As Exception
            ' 错误处理
            Console.WriteLine($"解析错误: {ex.Message}")
        End Try

        Return (address, longitude, latitude)
    End Function
End Module